<!doctype html>
<html lang="en">
<head>
	<title>Many Cameras (Three.js)</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<link rel=stylesheet href="css/base.css"/>
</head>
<body>

<p id="errorMessage"></p>

<video id="monitor" autoplay width="160" height="120" style="visibility: hidden; float:left;"></video>
<canvas id="videoImage" width="160" height="120" style="visibility: hidden; float:left;"></canvas>

<script>
navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.getUserMedia;
window.URL = window.URL || window.webkitURL;

var camvideo = document.getElementById('monitor');

	if (!navigator.getUserMedia) 
	{
		document.getElementById('errorMessage').innerHTML = 
			'Sorry. <code>navigator.getUserMedia()</code> is not available.';
		return;
	}
	navigator.getUserMedia({video: true}, gotStream, noStream);

function gotStream(stream) 
{
	if (window.URL) 
	{   camvideo.src = window.URL.createObjectURL(stream);   } 
	else // Opera
	{   camvideo.src = stream;   }

	camvideo.onerror = function(e) 
	{   stream.stop();   };

	stream.onended = noStream;
}

function noStream(e) 
{
	var msg = 'No camera available.';
	if (e.code == 1) 
	{   msg = 'User denied access to use camera.';   }
	document.getElementById('errorMessage').textContent = msg;
}
</script>

<script src="js/Three58.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/THREEx.KeyboardState.js"></script>
<script src="js/THREEx.FullScreen.js"></script>
<script src="js/THREEx.WindowResize.js"></script>

<!-- jQuery code to display an information button and box when clicked. -->
<script src="js/jquery-1.9.1.js"></script>
<script src="js/jquery-ui.js"></script>
<link rel=stylesheet href="css/jquery-ui.css" />
<link rel=stylesheet href="css/info.css"/>
<script src="js/info.js"></script>
<div id="infoButton"></div>
<div id="infoBox" title="Demo Information">
Movement controls: <br/>
<ul>
<li>W/S: Translate Forward/Backward
<li>A/D: Rotate Left/Right
<li>Q/E: Translate Left/Right
</ul>
This three.js demo is part of a collection at
<a href="http://stemkoski.github.io/Three.js/">http://stemkoski.github.io/Three.js/</a>
</div>
<!-- ------------------------------------------------------------ -->

<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
<script>
/*
	Three.js "tutorials by example"
	Author: Lee Stemkoski
	Date: July 2013 (three.js v59dev)
*/
	
// MAIN

// standard global variables
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();

// custom global variables
var video, videoImage, videoImageContext, videoTexture;

var mirrorSphere, mirrorSphereCamera; // for mirror material

var player = new THREE.Object3D();

init();
animate();

// FUNCTIONS 		
function init() 
{
	// SCENE
	scene = new THREE.Scene();
	// CAMERA
	var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
	var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
	camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
	scene.add(camera);
	camera.position.set(0,0,400);
	camera.lookAt(scene.position);	
	// RENDERER
	if ( Detector.webgl )
		renderer = new THREE.WebGLRenderer( { antialias:true } );
	else
		renderer = new THREE.CanvasRenderer(); 
	renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
	container = document.createElement( 'div' );
	// CSS added so the hidden HTML elements do not reposition this one
	container.style.cssText = "position:absolute;top:0px;left:0px;";
	document.body.appendChild( container );
	container.appendChild( renderer.domElement );
	// CONTROLS
	// disabled for manual repositioning of camera.
	//  controls = new THREE.TrackballControls( camera );
	// EVENTS
	THREEx.WindowResize(renderer, camera);
	THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
	// STATS
	stats = new Stats();
	stats.domElement.style.position = 'absolute';
	stats.domElement.style.bottom = '0px';
	stats.domElement.style.zIndex = 100;
	container.appendChild( stats.domElement );
	// LIGHT
	var light = new THREE.PointLight(0xffffff);
	light.position.set(0,250,0);
	scene.add(light);
	// FLOOR
	var floorTexture = new THREE.ImageUtils.loadTexture( 'images/checkerboard.jpg' );
	floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; 
	floorTexture.repeat.set( 10, 10 );
	var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
	var floorGeometry = new THREE.CircleGeometry(500, 20);
	// var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
	var floor = new THREE.Mesh(floorGeometry, floorMaterial);
	floor.position.y = -0.5;
	floor.rotation.x = Math.PI / 2;
	scene.add(floor);
	// SKYBOX/FOG
	var materialArray = [];
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'images/dawnmountain-xpos.png' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'images/dawnmountain-xneg.png' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'images/dawnmountain-ypos.png' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'images/dawnmountain-yneg.png' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'images/dawnmountain-zpos.png' ) }));
	materialArray.push(new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'images/dawnmountain-zneg.png' ) }));
	for (var i = 0; i < 6; i++)
	   materialArray[i].side = THREE.BackSide;
	var skyboxMaterial = new THREE.MeshFaceMaterial( materialArray );
	var skyboxGeom = new THREE.CubeGeometry( 5000, 5000, 5000, 1, 1, 1 );
	var skybox = new THREE.Mesh( skyboxGeom, skyboxMaterial );
	scene.add( skybox );	
	
	scene.add( player );
	
	///////////
	// VIDEO //
	///////////

	video = document.getElementById( 'monitor' );
	
	videoImage = document.getElementById( 'videoImage' );
	videoImageContext = videoImage.getContext( '2d' );
	// background color if no video present
	videoImageContext.fillStyle = '#000000';
	videoImageContext.fillRect( 0, 0, videoImage.width, videoImage.height );

	videoTexture = new THREE.Texture( videoImage );
	videoTexture.minFilter = THREE.LinearFilter;
	videoTexture.magFilter = THREE.LinearFilter;
	
	var movieMaterial = new THREE.MeshBasicMaterial( { map: videoTexture, overdraw: true, side:THREE.DoubleSide } );
	// the geometry on which the movie will be displayed;
	var movieGeometry = new THREE.PlaneGeometry( 100, 100, 1, 1 );
	// attach video to a mesh that will move with the camera
	this.movieScreen = new THREE.Mesh( movieGeometry, movieMaterial );

	// add a frame to the image.
	var frameGeo = new THREE.CubeGeometry(120, 120, 20);
	var frameMat = new THREE.MeshLambertMaterial( {color:0x888888, emissive:0x000011} );
	this.frameMesh = new THREE.Mesh( frameGeo, frameMat );
	
	// "attach" player to camera
	player.position = camera.position;
	player.rotation = camera.rotation;
	player.add( movieScreen );
	player.add( frameMesh );
	// position the movieScreen so it is attached
	//   to the front and center of the frameMesh
	movieScreen.position.y = 5;
	movieScreen.position.z = -12;
	
	///////////////////
	// Mirror Sphere //
	///////////////////
	var sphereGeom =  new THREE.SphereGeometry( 50, 64, 32 ); // radius, segmentsWidth, segmentsHeight
	mirrorSphereCamera = new THREE.CubeCamera( 0.1, 5000, 256 );
	mirrorSphereCamera.renderTarget.minFilter = THREE.LinearMipMapLinearFilter;
	scene.add( mirrorSphereCamera );
	var mirrorSphereMaterial = new THREE.MeshPhongMaterial( { emissive: 0x8888aa, envMap: mirrorSphereCamera.renderTarget } );
	mirrorSphere = new THREE.Mesh( sphereGeom, mirrorSphereMaterial );
	mirrorSphere.position.set(0, 50, 0);
	mirrorSphereCamera.position = mirrorSphere.position;
	scene.add(mirrorSphere);

	camera.position.set(114,50,-61);
	camera.rotation.set(3.14, 0.875, 3.14);
	
	//////////////////////
	// Secondary camera //
	//////////////////////
	this.topCamera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR );
	scene.add(topCamera);
	topCamera.position.set(0,200+50,550+200);
	topCamera.lookAt(scene.position);
	
	// other camera stuff
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.setClearColor( 0x000000, 1 );
	renderer.autoClear = false;
	
}

function animate() 
{
    requestAnimationFrame( animate );
	render();		
	update();
}

function update()
{		
	if ( keyboard.pressed("p") ) // pause webcam
		video.pause();
	if ( keyboard.pressed("r") ) // resume webcam
		video.play();
		
	var delta = clock.getDelta(); // seconds.
	var moveDistance = 100 * delta; // 100 pixels per second
	var rotateAngle = Math.PI / 2 * delta;   // pi/2 radians (90 degrees) per second

	var previousPosition = camera.position.clone();
	var previousRotation = camera.rotation.clone();
	
	// move forwards/backwards/left/right (local coordinates)
	if ( keyboard.pressed("W") )
		camera.translateZ( -moveDistance );
	if ( keyboard.pressed("S") )
		camera.translateZ(  moveDistance );
	if ( keyboard.pressed("Q") )
		camera.translateX( -moveDistance );
	if ( keyboard.pressed("E") )
		camera.translateX(  moveDistance );	

	// rotate left/right/up/down
	if ( keyboard.pressed("A") )
		camera.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle);
	if ( keyboard.pressed("D") )
		camera.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle);
	
	var d = camera.position.distanceTo( mirrorSphere.position );
	if ( d > 500 || d < 60 )
	{
		camera.position = previousPosition;
		camera.rotation = previousRotation;
		player.position = camera.position;
		player.rotation = camera.rotation;
	}
	
	stats.update();
}

function render() 
{	
	// update image from webcam
	if ( video.readyState === video.HAVE_ENOUGH_DATA ) 
	{
		videoImageContext.drawImage( video, 0, 0, videoImage.width, videoImage.height );
		if ( videoTexture ) 
			videoTexture.needsUpdate = true;
	}

	// update cameras
	var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
	camera.aspect = 0.5 * SCREEN_WIDTH / SCREEN_HEIGHT;
	camera.updateProjectionMatrix();
	topCamera.aspect = 0.5 * SCREEN_WIDTH / SCREEN_HEIGHT;
	topCamera.updateProjectionMatrix();

	// setViewport parameters:
	//  lower_left_x, lower_left_y, viewport_width, viewport_height
	renderer.setViewport( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
	//renderer.clear();
	
	// left side
	renderer.setViewport( 1, 1,   0.5 * SCREEN_WIDTH - 2, SCREEN_HEIGHT - 2 );
	
	// move the CubeCamera to the position of the object
	//    that has a reflective surface, "take a picture" in each direction
	//    and apply it to the surface.
	// need to hide mirror surface before and after so that it does not
	//    "get in the way" of the camera
	movieScreen.visible = true;
	mirrorSphere.visible = false;
	
	renderer.autoClear = true;		
	mirrorSphereCamera.updateCubeMap( renderer, scene );
	renderer.autoClear = false;
	
	mirrorSphere.visible = true;
	movieScreen.visible = false;
	renderer.render( scene, camera );

	// right side
	movieScreen.visible = true;
	mirrorSphere.visible = true;
	renderer.setViewport( 0.5 * SCREEN_WIDTH + 1, 1,   0.5 * SCREEN_WIDTH - 2, SCREEN_HEIGHT - 2 );
	renderer.render( scene, topCamera );	
}

</script>

</body>
</html>
